using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Collections;

using EnvDTE;

using Widgetsphere.Generator.Common.Util;
using Widgetsphere.Generator.Common.Logging;
using System.Windows.Forms;

namespace Widgetsphere.Generator.Common.GeneratorFramework
{
	public class GeneratorHelper
	{
		#region Class Members

		#endregion

		#region Events

		public event ProjectItemGeneratedEventHandler ProjectItemGenerated;
		public event ProjectItemGenerationCompleteEventHandler GenerationComplete;
		public event ProjectItemGeneratedEventHandler ProjectItemGeneratedError;

		protected virtual void OnProjectItemGenerated(object sender, ProjectItemGeneratedEventArgs pigArgs)
		{
			if (ProjectItemGenerated != null)
			{
				this.ProjectItemGenerated(sender, pigArgs);
			}
		}

		protected virtual void OnGenerationComplete(object sender, ProjectItemGenerationCompleteEventArgs args)
		{
			if (GenerationComplete != null)
			{
				this.GenerationComplete(sender, args);
			}
		}

		protected virtual void OnProjectItemGeneratedError(object sender, ProjectItemGeneratedEventArgs pigArgs)
		{
			if (ProjectItemGeneratedError != null)
			{
				this.ProjectItemGeneratedError(sender, pigArgs);
			}
		}

		#endregion

		#region Model Methods
		private const string ROOT_NODE = "model";

		public static IGenerator OpenModel(string filePath)
		{
			IGenerator retVal = null;
			FileInfo file = new FileInfo(filePath);
			string xmlAttributeAssembleValue = "";
			if (file.Exists)
			{
				try
				{
					XmlDocument xDoc = new XmlDocument();
					string type = null;
					FileInfo assemblyName = null;
					if (file.Length == 0)
					{
						type = "Widgetsphere.Generator.WidgetsphereGeneratorProject";
						xmlAttributeAssembleValue = "Widgetsphere.Generator.dll";
						xDoc.LoadXml("<model guid=\"" + Guid.NewGuid().ToString() + "\" type=\"" + type + "\" assembly=\"" + xmlAttributeAssembleValue + "\"><ModelRoot key=\"" + Guid.NewGuid().ToString() + "\" projectName=\"[NEW PROJECT]\" useUTCTime=\"False\" version=\"0.0.0.0\" companyName=\"[COMPANY NAME]\" companyAbbreviation=\"ZZZ\" createdDate=\"" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\"><database key=\"" + Guid.NewGuid().ToString() + "\" databaseName=\"[DATABASE]\"></database></ModelRoot></model>");
						Uri assemblyUri = null;
						if (assemblyUri != null) assemblyName = new FileInfo(assemblyUri.AbsolutePath);
						else assemblyName = new FileInfo(xmlAttributeAssembleValue);
					}
					else
					{
						xDoc.Load(file.FullName);
						type = Common.Util.XmlHelper.GetAttributeValue(xDoc.DocumentElement, "type", "");
						xmlAttributeAssembleValue = Common.Util.XmlHelper.GetAttributeValue(xDoc.DocumentElement, "assembly", "");
						Uri assemblyUri = null;
						try { assemblyUri = new Uri(xmlAttributeAssembleValue); }
						catch { }
						if (assemblyUri != null) assemblyName = new FileInfo(assemblyUri.AbsolutePath);
						else assemblyName = new FileInfo(xmlAttributeAssembleValue);
					}					

					string assemblyFile = Path.Combine(AddinAppData.Instance.ExtensionDirectory, assemblyName.Name);
					FileInfo currentAssemblyFile = new FileInfo(assemblyFile);
					if (currentAssemblyFile.Exists)
					{
						retVal = (IGenerator)ReflectionHelper.CreateInstance(currentAssemblyFile.FullName, type);
						retVal.XmlLoad(xDoc.DocumentElement);
						retVal.FileName = filePath;
					}
					else
					{
						GlobalHelper.ShowError("The model cannot be opened. You do not have the appropriate assembly. " + currentAssemblyFile.FullName);
					}
				}
				catch (Exception ex)
				{
					throw new Exception(file.FullName + " does not have the correct format.", ex);
				}
			}
			else
			{
				throw new Exception("File does not exist:");
			}
			return retVal;
		}

		public static void SaveModelFile(IGenerator generatorProject, string fullFileName)
		{
			GeneratorAttribute att = (GeneratorAttribute)ReflectionHelper.GetSingleAttribute(typeof(GeneratorAttribute), generatorProject);

			XmlDocument xmlDoc = new XmlDocument();
			xmlDoc.LoadXml(string.Format("<{0}></{0}>", ROOT_NODE));

			XmlAttribute xmlAttr = xmlDoc.CreateAttribute("guid");
			xmlAttr.Value = att.ProjectGuid.ToString();
			xmlDoc.DocumentElement.Attributes.Append(xmlAttr);

			XmlAttribute typeAttribute = xmlDoc.CreateAttribute("type");
			typeAttribute.Value = generatorProject.GetType().FullName;
			xmlDoc.DocumentElement.Attributes.Append(typeAttribute);

			XmlAttribute assemblyAttribute = xmlDoc.CreateAttribute("assembly");
			assemblyAttribute.Value = new FileInfo(generatorProject.GetType().Assembly.Location).Name;
			xmlDoc.DocumentElement.Attributes.Append(assemblyAttribute);

			generatorProject.XmlAppend(xmlDoc.DocumentElement);
			xmlDoc.Save(fullFileName);
		}

		public static ArrayList GetModels(ProjectItem projectItem)
		{
			ArrayList al = new ArrayList();
			foreach (ProjectItem pi in projectItem.ProjectItems)
			{
				if (IsModelFile(pi))
				{
					al.Add(pi);
				}
				al.AddRange(GetModels(pi));
			}
			return al;
		}
		public static bool IsModelFile(ProjectItem projectItem)
		{
			string fullName = projectItem.get_FileNames(1);
			return IsModelFile(fullName);
		}

		public static bool IsModelFile(string fileName)
		{
			string ext = System.IO.Path.GetExtension(fileName);
			if ((string.Compare(ext, ".wsgen", true) != 0))
				return false;
			else
				return true;
		}

		#endregion

		#region Generation Methods

		#region Public Generate Methods

		public IGenerator _generator;
		public void GenerateAll(IGenerator generator, List<Type> excludeList)
		{
			try
			{
				GlobalCacheFile globalCacheFile = new GlobalCacheFile();
				_generator = generator;
				List<Type> projectGenerators = GetProjectGenerators(generator);
				foreach (Type projectGeneratorType in projectGenerators)
				{
					try
					{
						bool exclude = false;
						foreach (string key in globalCacheFile.ExcludeList)
						{
							if (key == projectGeneratorType.FullName)
								exclude = true;
						}

						//string excludeFile = projectGeneratorType.Assembly.Location;
						//FileInfo fi = new FileInfo(excludeFile);
						//excludeFile = fi.Directory.FullName;
						//excludeFile = Path.Combine(excludeFile, "ProjectExcludes.xml");
						//if (File.Exists(excludeFile))
						//{
						//  XmlDocument document = new XmlDocument();
						//  document.Load(excludeFile);

						//  XmlNodeList list = document.SelectNodes("//excludes/item");
						//  foreach (XmlNode node in list)
						//  {
						//    if (node.InnerText == projectGeneratorType.FullName)
						//      exclude = true;
						//  }
						//}

						//Check the passed in exclude list
						if (excludeList.Contains(projectGeneratorType))
							exclude = true;

						if (!exclude)
							GenerateProject(generator, projectGeneratorType);

					}
					catch (Exception ex)
					{
						System.Diagnostics.Debug.WriteLine(ex);
						MessageBox.Show(ex.ToString(), "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
					}
				}

				System.Diagnostics.Debug.Write("");

			}
			catch (Exception ex)
			{
				throw;
			}

		}

		public void GenerateProject(IGenerator generator, Type projectGeneratorType)
		{
			try
			{
				IProjectGenerator projectGenerator = GetProjectGenerator(projectGeneratorType);
				projectGenerator.Initialize(generator.RootController.Object);
				if (!EnvDTEHelper.Instance.ProjectExists(projectGenerator.ProjectName))
				{
					CreateProject(generator, projectGeneratorType);
				}
				GenerateProjectItems(projectGenerator);
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public void CreateProject(IGenerator generator, Type projectGeneratorType)
		{
			try
			{
				IProjectGenerator projectGenerator = GetProjectGenerator(projectGeneratorType);
				projectGenerator.Initialize(generator.RootController.Object);
				projectGenerator.CreateProject();
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public IProjectGenerator GetProjectGenerator(Type projectGeneratorType)
		{
			try
			{
				IProjectGenerator projectGenerator = (IProjectGenerator)ReflectionHelper.CreateInstance(projectGeneratorType);
				return projectGenerator;
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public IProjectItemGenerator GetProjectItemGenerator(Type projectItemGeneratorType)
		{
			try
			{
				IProjectItemGenerator projectGenerator = (IProjectItemGenerator)ReflectionHelper.CreateInstance(projectItemGeneratorType);
				return projectGenerator;
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public void GenerateProjectItems(IProjectGenerator projectGenerator)
		{
			try
			{
				List<Type> projectItemGenerators = GetProjectItemGenerators(projectGenerator);
				foreach (Type projectItemGeneratorType in projectItemGenerators)
				{
					try
					{
						GenerateProjectItems(projectItemGeneratorType);
					}
					catch (Exception ex)
					{
						WSLog.LogWarning(ex);
					}
				}
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public void GenerateSubItems(IProjectItemGenerator projectItemGenerator)
		{
			try
			{
				List<Type> projectItemGenerators = GetProjectItemGenerators(projectItemGenerator);
				foreach (Type projectItemGeneratorType in projectItemGenerators)
				{
					try
					{
						GenerateProjectItems(projectItemGeneratorType);
					}
					catch (Exception ex)
					{
						WSLog.LogWarning(ex);
					}
				}
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public void GenerateProjectItems(Type projectItemGeneratorType)
		{
			try
			{
				IProjectItemGenerator projectItemGenerator = GetProjectItemGenerator(projectItemGeneratorType);
				projectItemGenerator.Initialize(_generator.RootController.Object);
				GenerateProjectItems(projectItemGenerator);
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public void GenerateProjectItems(IProjectItemGenerator projectItemGenerator)
		{
			try
			{
				projectItemGenerator.GenerationComplete += new ProjectItemGenerationCompleteEventHandler(projectItemGenerator_GenerationComplete);
				projectItemGenerator.ProjectItemGenerated += new ProjectItemGeneratedEventHandler(projectItemGenerator_ProjectItemGenerated);
				projectItemGenerator.Generate();
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		public List<Type> GetProjectGenerators(IGenerator generator)
		{
			return GetGeneratorsImpl(generator);
		}

		public List<Type> GetProjectItemGenerators(IProjectGenerator projectGenerator)
		{
			return GetGeneratorsImpl(projectGenerator);
		}

		public List<Type> GetProjectItemGenerators(IProjectItemGenerator projectItemGenerator)
		{
			return GetGeneratorsImpl(projectItemGenerator);
		}
		#endregion

		#region private helpers

		List<string> processedFiles = new List<string>();
		private void projectItemGenerator_ProjectItemGenerated(object sender, ProjectItemGeneratedEventArgs e)
		{
			try
			{
				Project project = EnvDTEHelper.Instance.GetProject(e.ProjectName);
				ProjectItem parent = EnvDTEHelper.Instance.GetProjectItem(e.ProjectName, e.ParentItemName, e.ParentItemType);

				FileStateInfo fileStateInfo = new FileStateInfo();
				if (e.ParentItemName != string.Empty)
				{
					ProjectItem newItem = null;
					if (e.ContentType == ProjectItemContentType.String)
						newItem = EnvDTEHelper.Instance.AddProjectItem(parent, e.ProjectItemName, e.ProjectItemContent, e.Overwrite, out fileStateInfo);
					else
						newItem = EnvDTEHelper.Instance.AddProjectItem(parent, e.ProjectItemContent, out fileStateInfo);

					if (fileStateInfo.FileState == EnvDTEHelper.FileStateConstants.Success)
						EnvDTEHelper.SetProperties(newItem, e.Properties);
				}
				else
				{
					ProjectItem projectItem = null;
					if (e.ContentType == ProjectItemContentType.String)
						projectItem = EnvDTEHelper.AddProjectItem(project, e.ProjectItemContent, e.ProjectItemName, e.Overwrite, out fileStateInfo);
					else
						projectItem = EnvDTEHelper.AddProjectItem(project, e.ProjectItemContent, out fileStateInfo);

					if (fileStateInfo.FileState == EnvDTEHelper.FileStateConstants.Success)
						EnvDTEHelper.SetProperties(projectItem, e.Properties);
				}

				//TEMP
				processedFiles.Add(fileStateInfo.FileName);
				//TEMP

				#region Compute CRC
				//System.IO.StreamReader sr = System.IO.File.OpenText("");
				//Widgetsphere.Generator.Common.Util.CRC32HashAlgorithm hash = new CRC32HashAlgorithm();
				//byte[] arr = hash.ComputeHash(sr.BaseStream);
				//sr.Close();
				//XmlHelper.AddAttribute(newfileNode, "crc", "0");
				#endregion

				if (fileStateInfo.FileName == "")
				{
					System.Diagnostics.Debug.Write("");
				}
				if (fileStateInfo.FileState == EnvDTEHelper.FileStateConstants.Failed)
				{
					System.Diagnostics.Debug.Write("");
				}

				//Write Log
				WSLog.LogInfo("Project Item Generated: {0}", e.ProjectItemName);
				e.FileState = fileStateInfo.FileState;
				e.FullName = fileStateInfo.FileName;
				this.OnProjectItemGenerated(sender, e);

			}
			catch (Exception ex)
			{
				this.OnProjectItemGeneratedError(this, e);
				WSLog.LogWarning(ex);
			}
		}

		private void projectItemGenerator_GenerationComplete(object sender, ProjectItemGenerationCompleteEventArgs e)
		{
			WSLog.LogInfo("Project Item Generation Complete: {0}", e.ProjectItemGenerator);
			try
			{
				GenerateSubItems(e.ProjectItemGenerator);
				this.OnGenerationComplete(sender, e);
			}
			catch (Exception ex)
			{
				WSLog.LogWarning(ex);
			}
		}

		private List<Type> GetGeneratorsImpl(object parent)
		{
			List<Type> returnVal = new List<Type>();
			Type[] generatorTypes = null;
			if (ReflectionHelper.ImplementsInterface(parent, typeof(IGenerator)))
				generatorTypes = ReflectionHelper.GetCreatableObjectImplementsInterface(typeof(IProjectGenerator), AddinAppData.Instance.ExtensionDirectory);
			else
				generatorTypes = ReflectionHelper.GetCreatableObjectImplementsInterface(typeof(IProjectItemGenerator), AddinAppData.Instance.ExtensionDirectory);

			if (generatorTypes.Length == 0)
			{
				MessageBox.Show("There was an error getting generators from the following path. '" + AddinAppData.Instance.ExtensionDirectory + "'", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}

			foreach (Type type in generatorTypes)
			{
				GeneratorItemAttribute genItemAttribute = (GeneratorItemAttribute)ReflectionHelper.GetSingleAttribute(typeof(GeneratorItemAttribute), type);
				if (genItemAttribute.ParentType == parent.GetType())
				{
					returnVal.Add(type);
				}
			}
			return returnVal;
		}

		#endregion

		#endregion
	}
}